package com.lagou.context;

import com.lagou.annotation.Autowired;
import com.lagou.annotation.Service;
import com.lagou.annotation.Transactional;
import com.lagou.factory.BeanFactory;
import com.lagou.factory.ProxyFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.reflections.Reflections;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author: lcz
 * @date: Created in 18:28 2020/10/5
 * @description: 1、解析xml，通过反射技术实例化对象并存储待用 2、对外提供获取实例对象的接口
 */
public class XmlApplicationContext implements BeanFactory {
    private static Map<String, Object> map = new HashMap<>();

    public XmlApplicationContext() {}

    public XmlApplicationContext(String configName) {
        this.refresh(configName);
    }


    /**
     * @param id
     * @author: lcz
     * @date: Created in 13:35 2020/10/5
     * @description: 根据id获取实例对象
     */
    @Override
    public Object getBean(String id) {
        return map.get(id);
    }

    /**
     * @param configName 配置文件名称
     * @author: lcz
     * @date: Created in 10:52 2020/10/6
     * @description: bean 初始化
     */
    public void refresh(String configName) {
        //加载xml
        InputStream inputStream = XmlApplicationContext.class.getClassLoader().getResourceAsStream(configName);
        //解析xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//component-scan");
            //如果获取到的component-scan节点不止一个，则取最后一个
            if (beanList != null && beanList.size() > 0) {
                Element element = beanList.get(beanList.size() - 1);
                //解析@service注解
                this.loadServiceToIOC(element);
                //实例化完成之后维护对象的依赖关系及事务处理
                this.dependencyOn();
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param
     * @author: lcz
     * @date: Created in 10:32 2020/10/6
     * @description: 解析@service注解
     */
    private void loadServiceToIOC(Element element) throws IllegalAccessException, InstantiationException {
        //获取到该元素的base-package属性
        String basePackage = element.attributeValue("base-package");
        //扫描获取反射对象集合
        Reflections reflections = new Reflections(basePackage);
        //获取注解为service的集合
        Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(Service.class);
        for (Class<?> aClass : classSet) {
            //实例化对象
            Object obj = aClass.newInstance();
            Service annotation = aClass.getAnnotation(Service.class);
            //对象ID在service注解有value时用value，没有时用类名
            if (null == annotation.value() || "".equals(annotation.value())) {
                //由于getName获取的是全限定类名，需要分割去掉包名部分
                String[] names = aClass.getName().split("\\.");
                String id = names[names.length - 1];
                map.put(this.toLowerCaseFirstOne(id), obj);
            } else {
                map.put(annotation.value(), obj);
            }
        }
    }

    /**
     * @author: lcz
     * @date: Created in 10:45 2020/10/6
     * @description: 维护对象的依赖关系及事务处理
     */
    private void dependencyOn() throws IllegalAccessException {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Object obj = entry.getValue();
            Class<?> aClass = obj.getClass();
            //实例化完成之后维护对象的依赖关系
            this.loadAutowiredToIOC(aClass, obj);
            //事务处理
            obj = this.postProcessorsTransactional(aClass, obj);
            //将被维护过依赖关系的对象重新存储到map中
            map.put(entry.getKey(), obj);
        }
    }

    /**
     * @param aClass
     * @param obj
     * @author: lcz
     * @date: Created in 10:48 2020/10/6
     * @description: 维护对象的依赖关系
     */
    private void loadAutowiredToIOC(Class<?> aClass, Object obj) throws IllegalAccessException {
        //获取所有的属性
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            //如果有注解Autowired，并且属性值是true，则注入
            if (field.isAnnotationPresent(Autowired.class) && field.getAnnotation(Autowired.class).required()) {
                String[] names = field.getType().getName().split("\\.");
                String id = names[names.length - 1];
                field.setAccessible(true);
                field.set(obj, map.get(this.toLowerCaseFirstOne(id)));
            }
        }
    }

    /**
     * @param aClass
     * @param obj
     * @author: lcz
     * @date: Created in 10:49 2020/10/6
     * @description: 事务处理
     */
    private Object postProcessorsTransactional(Class<?> aClass, Object obj) {
        //判断对象类是否持有Transactional注解，若有，则修改对象为代理对象
        if (aClass.isAnnotationPresent(Transactional.class)) {
            //获取代理工厂proxyFactory
            ProxyFactory proxyFactory = (ProxyFactory) this.getBean("proxyFactory");
            //判断对象是否是通过接口实现
            //获取类实现的所有接口
            Class<?>[] interfaces = aClass.getInterfaces();
            if (null != interfaces && interfaces.length > 0) {
                //用JDK动态代理
                obj = proxyFactory.getJdkProxy(obj);
            } else {
                //用Cglib动态代理
                obj = proxyFactory.getCglibProxy(obj);
            }
        }
        return obj;
    }

    /**
     * @param id
     * @author: lcz
     * @date: Created in 10:27 2020/10/6
     * @description: 首字母转小写
     */
    private String toLowerCaseFirstOne(String id) {
        if (Character.isLowerCase(id.charAt(0))) {
            return id;
        } else {
            return (new StringBuffer())
                    .append(Character.toLowerCase(id.charAt(0)))
                    .append(id.substring(1)).toString();
        }
    }
}
